package com.xiao.code.parse;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import com.alibaba.fastjson.JSON;

import japa.parser.ast.CompilationUnit;
import japa.parser.ast.ImportDeclaration;
import japa.parser.ast.Node;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.expr.MethodCallExpr;
import japa.parser.ast.expr.NameExpr;
import japa.parser.ast.expr.VariableDeclarationExpr;
import japa.parser.ast.stmt.ExpressionStmt;
import japa.parser.ast.visitor.VoidVisitorAdapter;

public class Start {
	
	public static JavaMap clientMap = new JavaMap();
	public static JavaMap proMap = new JavaMap();

	public static String outPackge = null;
	
	public static String soaPackage = "E:\\lyf\\codetest\\reso\\osoa";

	public static String soaModelName = "odss-business";
	
	public static String rpStr = "ffdjsaklfjsdjljfdsal";
	
	// 系统变量
	public static Set<String> domainSet = new HashSet<>();
	public static Set<String> serviceSet = new HashSet<>();

	
	/**
	 * 分析所引用的客户端
	 */
	//@Test
	public  void analysisClientPage(){
		
		
		Set<String> clientSet = new HashSet<>();
		
		PoolDes  mastPool = new PoolDes();
		mastPool.poolName = "odss";
		mastPool.poolPath = "E:\\lyf\\dev20180827";
		proMap.load(Arrays.asList(mastPool),false);
		
		proMap.javaMap.keySet().forEach(key -> {
			List<ImportDeclaration> list = proMap.javaMap.get(key).getJavaParser().getImports();
			
			if (list == null){
				return ;
			}
			
			list.forEach(i ->{
				
				String importName = i.getName().toString();
				
				if (importName.contains(".client.") && importName.startsWith("com.odianyun")){
					clientSet.add(importName.substring(0,importName.lastIndexOf(".client.") + ".client.".length()));
				}
				
			});
			
			
		});
		
		System.out.println(JSON.toJSONString(clientSet));
		System.out.println("完成");
		
		
	}
	

	@Test
	public  void start ()throws Exception {
		
		
		outPackge = "com.odianyun.odss.soa";

		String outPath = "E:\\e\\dev_zidong_osoa\\";
		
		
		// 客户端路径
		List<PoolDes> clistPathList   = new ArrayList<>();
		{
			PoolDes  pool = new PoolDes();
			pool.poolName = "order";
			pool.poolPath = "E:\\idea_work\\LYF\\back-order";
			clistPathList.add(pool);
		}
		
		{
			PoolDes  pool = new PoolDes();
			pool.poolName = "merchant";
			pool.poolPath = "E:\\idea_work\\merchant_dev_2";
			clistPathList.add(pool);
		}
		
		{
			PoolDes  pool = new PoolDes();
			pool.poolName = "product";
			pool.poolPath = "E:\\idea_work\\product_dev";
			clistPathList.add(pool);
		}
		
		{
			PoolDes  pool = new PoolDes();
			pool.poolName = "stock";
			pool.poolPath = "E:\\new_work\\LYF\\basics-stock-cp";
			clistPathList.add(pool);
		}
		
		clientMap.load(clistPathList,true);
		
		PoolDes  mastPool = new PoolDes();
		mastPool.poolName = "odss";
		mastPool.poolPath = "E:\\lyf\\dev20180827";
		proMap.load(Arrays.asList(mastPool),false);
		proMap.loadEndInit();
		

		
		// 加载osa库
		PoolDes  osaPool = new PoolDes();
		osaPool.poolName = "odss";
		osaPool.poolPath = "E:\\lyf\\codetest\\reso\\osoa";
		proMap.loadJavaFile(soaModelName,osaPool,new File(soaPackage));
		

		
		
		System.out.println("=============== 加载完成");
		
		// 初始化soa
		initSoa();
		
		System.out.println("初始化完成=============");

		System.out.println("处理完成==========");
		
		
		File[] of = new File(outPath).listFiles();
		
		if (of == null || of.length == 0){
			FileUtils.copyDirectory(new File(mastPool.poolPath),  new File(outPath));
		}

		// 复制项目
		doFillClient();

		proMap.doSave(outPath);
		
		// 填充枚举
		{
			JavaDescribe domainEnumDesc = proMap.javaMap.get(proMap.findClass("DomainEnum"));
			
			String domainStr = domainEnumDesc.getJavaParser().toString().replace("daima", "daima\n" + StringUtils.join(domainSet,",\n"));
			String fPath = outPath + File.separator + domainEnumDesc.modelName +  File.separator +  "src"+File.separator+"main"+File.separator+"java" + File.separator + domainEnumDesc.key.replace(".", File.separator) + ".java";
			FileUtils.writeStringToFile(new File(fPath), domainStr.replace(Start.rpStr + ";", ""));

		}
		
		{
			JavaDescribe serviceEnumDesc = proMap.javaMap.get(proMap.findClass("RemoteSoaServiceEnum"));

	        String serviceStr = serviceEnumDesc.getJavaParser().toString().replace("daima", "daima\n" + StringUtils.join(serviceSet,",\n"));
			String fPath = outPath + File.separator + serviceEnumDesc.modelName +  File.separator +  "src"+File.separator+"main"+File.separator+"java" + File.separator + serviceEnumDesc.key.replace(".", File.separator) + ".java";
			FileUtils.writeStringToFile(new File(fPath), serviceStr.replace(Start.rpStr + ";", ""));

		
		}
			
		
		
		
		System.out.println("完成");
		System.exit(0);
	}
	
	
	

	
	
	/**
	 * 初始化soa
	 */
	public static void initSoa(){
		
		List<File> fileList = loadJavaFile(new File(soaPackage));
		
		fileList.forEach( f -> {
			
			System.out.println("initSoa : " + f.getPath());
			JavaDescribe  javaDescribe = proMap.javaMap.get(proMap.findClass(f.getName().split("\\.")[0]));
			proMap.rnamePackage(javaDescribe, outPackge + "." + javaDescribe.key.substring(0 , javaDescribe.key.lastIndexOf(".")));
			
		});
		
		
	}
	
	
	/**
	 * 加载java文件
	 * @param file
	 * @return
	 */
	private static List<File> loadJavaFile(File file){
		
		List<File> list = new ArrayList<>();
		if (!file.isDirectory() && file.getName().endsWith(".java")){
			list.add(file);
		}else {
			Arrays.asList(file.listFiles()).stream()
			      .forEach(f -> list.addAll(loadJavaFile(f)));
		}
		
		return list;
		
	}
	
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void doFillClient() throws Exception{
		
		for (String key : Arrays.asList(proMap.javaMap.keySet().toArray(new String[0]))){
			
			
			JavaDescribe  javaDescibe =  proMap.javaMap.get(key);
			
			System.out.println("doFindClient:key =  " + key);
			
			CompilationUnit  compilationUnit = javaDescibe.getJavaParser();
			
			Set<String> rmImplSet = new HashSet<>();
			
			
			new VoidVisitorAdapter() {
		        @Override
		        public void visit(MethodDeclaration n, Object arg) {
		        	
		        	System.out.println("========" + key + "----" + n.getName());
		        	
		        	// 获取代码块
		        	List<Node> nodeList  = findList(n, Arrays.asList(VariableDeclarationExpr.class , MethodCallExpr.class))
						        			.stream().filter(e -> {
								        		try {
								        			String str = e.toString();
									        		if (str != null && (
									        				str.contains("Client.getInstanst")
									        				||
									        				str.contains("Client.getInstance")
									        				||
									        				str.contains("Client.newInstance")
									        				||
									        				str.contains("Client.getInstant")
									        				)
									        				){
									        			return true;
									        		}
												} catch (Exception ex) {
													ex.printStackTrace();
												}
								        		return false;
								        	}).collect(Collectors.toList());
		        
		        	if (nodeList == null || nodeList.size() == 0){
		        		return ;
		        	}
		        	
		        	
		        	for (Node valClintNode : nodeList){
		        		
		        		// 寻找client 
		        		String str = valClintNode.toString();
		        		String oi = str.split("=")[1];
		        		String[] os = oi.split("\\.");
		        		
		        		// 如果是直接调用
		        		if (os.length > 2){
		        			String methodName = null;
			        		String orgName = null;
			        		String claName = null;
			        		
		        			orgName =  os[2].substring(os[2].indexOf("(")+1, os[2].lastIndexOf(")"));
		        			claName = os[0];
		        			
		        			methodName = os[2].substring(0, os[2].indexOf("("));
		        			
		        			// 寻找client包
			        		String clientImpl = javaDescibe.findIm(claName);
		        			
		        			if(!clientMap.checkMovePool(clientImpl)){
		        				continue;
		        			}
		        			// 如果是需要迁移的包 
		        			Set<String> implSet = javaDescibe.doFindImpl(valClintNode);
		        			
		        			
		        			rmImplSet.add(clientImpl);
			        		
			        		
			        		// 获取返回类型
			        		String strType = str.split("=")[0].split(" ")[0].trim();
			        		
			        		// 创建枚举key
			        		String enumKey = createServiceEnum(javaDescibe, implSet,clientImpl , methodName, strType);
			        		
			        		String soaStr = str.split("=")[0] + "=" + "Soa.remoteSoa().call(RemoteSoaServiceEnum." + enumKey + " , " + orgName + ")";
			        		
		        			rpNode(valClintNode, soaStr);;
			        		
		        			
		        		}else {
		        			// 如果变量调用
		        			String claName = os[0];
		        			
		        			String  varName = str.split("=")[0].split(" ")[1].trim();
		        			
		        			// 寻找client包
			        		String clientImpl = javaDescibe.findIm(claName).trim();
		        			
		        			if(!clientMap.checkMovePool(clientImpl)){
		        				continue;
		        			}
		        			
		        			// 替换为空
		        			rpNode(valClintNode, rpStr);;
		        			
		        			javaDescibe.findBlockByVal(n, varName).forEach(e ->{
		        				
		        				// 代码块
		        				String bStr = e.toString(); 
		        				String[] bs = bStr.split("=");
		        				
		        				String[] csp = bs[1].split("\\.");
		        				
			        			String methodName = csp[1].substring(0, csp[1].indexOf("(")).trim();
			        			
			        			String orgName =  csp[1].substring(csp[1].indexOf("(")+1, csp[1].lastIndexOf(")")).trim();
				        		
				        		// 删除client包
			        			rmImplSet.add(clientImpl);
				        		
				        		// 获取返回类型
				        		String strType = bs[0].split(" ")[0].trim();
				        		
				        		// 寻找类型
				        		Set<String> implSet = javaDescibe.doFindImpl(e);
				        		
				        		// 创建枚举key
				        		String enumKey = createServiceEnum(javaDescibe, implSet,clientImpl , methodName, strType);
				        		
				        		String soaStr = bs[0] + "=" + " Soa.remoteSoa().call(RemoteSoaServiceEnum." + enumKey + " , " + orgName + ")";
				        		
				        		rpNode(e, soaStr);
		        				
		        			});
		        			
		        			
		        		}
		        	}
		        	
		        }
		    }.visit(compilationUnit, null);
		    
		   if(rmImplSet != null && rmImplSet.size() > 0){
			   // 添加引用
			    javaDescibe.addImpl(new HashSet<>(Arrays.asList(proMap.findClass("RemoteSoaServiceEnum"),proMap.findClass("Soa"))));
			    
			    // 删除包
			    rmImplSet.forEach(javaDescibe::rmImp);
			    
			    // 删除引入client
			    javaDescibe.findImpl(clientMap.poolPackageSet.stream().map(e -> e + ".client.").collect(Collectors.toSet()))
			               .forEach(javaDescibe::rmImp);
			 
		   }
			
			// 初始化类
			moveClass(javaDescibe);
			
			if (key.contains("stock") && key.contains("facade")){
				System.out.println("doFindClient2:key =  " + key);
			}
			
		}
		
	
		
	}
	
	
	public static void rpNode(Node node , String str){
		((ExpressionStmt)node.getParentNode()).setExpression(new NameExpr(str));
	}
	
	
  public static <T> List<T> find(Node parse ,Class<T>  cla, String name){
		
		List<T>  list =  find(parse,cla).stream()
	                                         // 寻找代码块
	                                         .filter(node -> node.toString().contains(name))
	                                         .collect(Collectors.toList());
    	
	    return list;
	}
	

  public static <T> List<T> find(Node node , Class<T> cla){
  	
  	List<T>  list = new ArrayList<>();
  	
  	if (node.getClass().equals(cla)){
  		list.add((T)node);
  	}else {
  		node.getChildrenNodes().forEach(e -> list.addAll(find(e,cla)));
  	}
  	
  	return list;
  	
  }
  
  
  public static  List<Node> findList(Node node , List<Class<?>> claList){
	  	
	  	List<Node>  list = new ArrayList<>();
	  	
	  	if (claList.contains(node.getClass())){
	  		list.add(node);
	  	}else {
	  		node.getChildrenNodes().forEach(e -> list.addAll(findList(e,claList)));
	  	}
	  	
	  	return list;
	  	
}
  
	
	/**
	 * 创建枚举
	 * @param javaDescribe
	 * @param impl
	 * @param serviceKey
	 * @param methodName
	 * @param typeStr
	 * @return
	 */
	public static String createServiceEnum(JavaDescribe javaDescribe , Set<String> implSet , String serviceKey , String methodName , String typeStr){
		
		JavaDescribe  serviceJava = JavaMap.javaAllMap.get(serviceKey);
		
		String domainName = (find(serviceJava.getJavaParser(),MethodCallExpr.class,"setDomainName" ).get(0)).getArgs().get(0).toString();

		String serviceAppName = (find(serviceJava.getJavaParser(),MethodCallExpr.class,"setServiceAppName" ).get(0)).getArgs().get(0).toString();
		
		String clientAppName = (find(serviceJava.getJavaParser(),MethodCallExpr.class,"setClientAppName" ).get(0)).getArgs().get(0).toString();
		String serviceName = (find(serviceJava.getJavaParser(),MethodCallExpr.class,"setServiceName" ).get(0)).getArgs().get(0).toString();
		String serviceVersion = (find(serviceJava.getJavaParser(),MethodCallExpr.class,"setServiceVersion" ).get(0)).getArgs().get(0).toString();

		String timeout = TimeUnit.SECONDS.toMillis(10) + "";
		String readTimeout = TimeUnit.MINUTES.toMillis(5) + "";
		
		try {
			 String timeoutTemp = (find(serviceJava.getJavaParser(),MethodCallExpr.class,"setTimeout" ).get(0)).getArgs().get(0).toString();
			 timeout = Long.valueOf(timeoutTemp) + "";
		} catch (Exception e) {
		}
		
		try {
			 String readTimeoutTemp = (find(serviceJava.getJavaParser(),MethodCallExpr.class,"setReadTimeout" ).get(0)).getArgs().get(0).toString();
			 readTimeout = Long.valueOf(readTimeoutTemp) + "";
		} catch (Exception e) {
		}


		
		// merchant("basics-merchantDomain", "back-merchant-web", "basics-merchant-client-read",  "0.1", 10000L, 5000L)
		
		String domainEnum = serviceJava.poolName.toUpperCase() + "(" + domainName + "," + serviceAppName + "," + clientAppName + "," + serviceVersion + "," + timeout + "L" + "," + readTimeout + "L" + ")";
		
		// 	queryMerchantByUserId(DomainEnum.merchant,"merchantReadService" , "queryMerchantByUserId" , new SoaTypeReference<OutputDTO<MerchantDTO>>(){} , "0.1", 10000L, 5000L, true ),

		String serviceEnumKey = serviceJava.poolName.toUpperCase() + "_" + serviceName.toUpperCase() + "_" + methodName.toUpperCase();  
		serviceEnumKey = serviceEnumKey.replace("\"", "");
		
		String serviceEnum = serviceEnumKey + "" + "(DomainEnum." + serviceJava.poolName.toUpperCase() + "," + serviceName + "," + "\"" + methodName + "\",new SoaTypeReference<" + typeStr + ">(){}," 
		                     + serviceVersion + "," + timeout + "L," + readTimeout + "L,true)"; 
		
		domainSet.add(domainEnum);
		serviceSet.add(serviceEnum);
		
		JavaDescribe serviceEnumDesc = proMap.javaMap.get(proMap.findClass("RemoteSoaServiceEnum"));

		// 初始化类
		initAndMoveClass(serviceEnumDesc, implSet);
		
		return serviceEnumKey;
		
	}
	
	/**
	 * 初始化，并且移动java类
	 * @param java
	 * @param implSet
	 */
	public static void initAndMoveClass(JavaDescribe java , Set<String> implSet){
		
		// 添加包
		java.addImpl(implSet);
		moveClass(java);
		
	}
	
	
	/**
	 * 移动class
	 * @param java
	 */
	private static void moveClass(JavaDescribe java){

		if(java == null){
			return;
		}

		if(clientMap.checkMovePool(java.packgeName)){
			
			if (java.key.equals("com.odianyun.basics.merchant.model.dto.ShopDTO")){
				System.out.println("aaa");
			}
			
			// 寻找同级包
			Set<String>  typeSet = java.doFindJavaType(java.getJavaParser());
			clientMap.findJava(java.packgeName + ".*")
			         .stream().filter(e -> e.substring(0, e.lastIndexOf(".")).equals(java.packgeName))  
			         .filter(e -> typeSet.stream()
							 .filter(e :: endsWith)
							 .count() > 0) // 找到相关包
			         .map(clientMap.javaMap::get) // 寻找相关类
			         .forEach(javaE -> {
			        	 javaE.modelName = soaModelName;
			        	 if ( clientMap.checkMovePool(javaE.getJavaParser().getPackage().getName().toString().trim())){
			        		    String nPackageName =  javaE.getJavaParser().getPackage().getName().toString().trim().replace("com.odianyun", outPackge +".dto");
								// 更改包名
								proMap.rnamePackage(javaE, nPackageName);
								
								moveClass(javaE);
			        		 
			        	 }
			        	 
							
			         }); //
		}
		
		
		// 移动包
		new ArrayList<>(java.getJavaParser().getImports()).forEach(i -> {
			String is = i.getName().toString().trim();

			// 如果包含在引入项目中
			if(clientMap.checkMovePool(is)){
				JavaDescribe impJava = clientMap.javaMap.get(is);
				
				if (impJava == null){
					if (i.isStatic()){
						is = is.substring(0,is.lastIndexOf("."));
						impJava = clientMap.javaMap.get(is);

						if (impJava == null){
							is = is.substring(0,is.lastIndexOf("."));
							impJava = clientMap.javaMap.get(is);

						}

					}
					impJava = clientMap.javaMap.get(is);
				}

				if(impJava == null){
					return;
				}

				// 初始化
				moveClass(impJava);
				impJava.modelName = soaModelName;
				if(!clientMap.checkMovePool(impJava.getJavaParser().getPackage().getName().toString().trim())){
				    return;
				}
				String nPackageName =  impJava.getJavaParser().getPackage().getName().toString().trim().replace("com.odianyun", outPackge +".dto");
				
				// 更改包名
				proMap.rnamePackage(impJava, nPackageName);
				
			}
		});
		
	
		
	}
	
	
	
	

}
